package com.dlc.shop.security.common.filter;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.dlc.shop.bean.model.SysAccessKey;
import com.dlc.shop.common.config.Constant;
import com.dlc.shop.common.enums.SysTypeEnum;
import com.dlc.shop.common.exception.YamiShopBindException;
import com.dlc.shop.common.handler.HttpHandler;
import com.dlc.shop.common.response.ResponseEnum;
import com.dlc.shop.common.response.ServerResponseEntity;
import com.dlc.shop.common.util.Json;
import com.dlc.shop.common.wrapper.ResponseWrapper;
import com.dlc.shop.security.common.adapter.AuthConfigAdapter;
import com.dlc.shop.security.common.bo.SignResponse;
import com.dlc.shop.security.common.bo.UidInfoBO;
import com.dlc.shop.security.common.manager.TokenStore;
import com.dlc.shop.security.common.util.AuthUserContext;
import com.dlc.shop.security.common.util.PmsContext;
import com.dlc.shop.security.common.util.SignUtils;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
 * 授权过滤，只要实现AuthConfigAdapter接口，添加对应路径即可：
 *
 * @author FrozenWatermelon
 * @date 2020/7/11
 */
@Component
@RequiredArgsConstructor
public class AuthFilter implements Filter {
    private static final Logger logger = LoggerFactory.getLogger(AuthFilter.class);
    private final AuthConfigAdapter authConfigAdapter;
    private final HttpHandler httpHandler;
    private final TokenStore tokenStore;
    private final SignUtils signUtils;
    @Value("${sa-token.token-name}")
    private String tokenName;
    private final ObjectMapper objectMapper;
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;
        String requestUri = req.getRequestURI();
        List<String> excludePathPatterns = authConfigAdapter.excludePathPatterns();
        AntPathMatcher pathMatcher = new AntPathMatcher();
        // 如果匹配不需要授权的路径，就不需要校验是否需要授权
        if (CollectionUtil.isNotEmpty(excludePathPatterns)) {
            for (String excludePathPattern : excludePathPatterns) {
                if (pathMatcher.match(excludePathPattern, requestUri)) {
                    chain.doFilter(req, resp);
                    return;
                }
            }
        }
        String accessToken = req.getHeader(tokenName);
        String grantType = req.getHeader(SignUtils.GRANT_TYPE);
        // 获取签名方式的接口传参类型,如果是纯数组类型需要传这个请求头为list,不是不用传
        String dataType = req.getHeader(SignUtils.DATA_TYPE);
        // 也许需要登录，不登陆也能用的uri
        // 比如优惠券接口，登录的时候可以判断是否已经领取过
        // 不能登录的时候会看所有的优惠券，等待领取的时候再登录
        boolean mayAuth = pathMatcher.match(AuthConfigAdapter.MAYBE_AUTH_URI, requestUri);
        String uid = null;
        try {
            // 通过签名访问
            if (Objects.equals(grantType, SignUtils.GRANT_TYPE_VALUE)) {
                signAccess(chain, req, resp,dataType);
                return;
            }
            // 如果有token，就要获取token
            else if (StrUtil.isNotBlank(accessToken)) {
                // 校验登录，并从缓存中取出用户信息
                try {
                    // token访问
                    uid = tokenStore.getUidByToken(accessToken);
                } catch (Exception e) {
                    httpHandler.printServerResponseToWeb(ServerResponseEntity.fail(ResponseEnum.UNAUTHORIZED));
                    return;
                }
            } else if (!mayAuth) {
                // 返回前端未授权
                httpHandler.printServerResponseToWeb(ServerResponseEntity.fail(ResponseEnum.UNAUTHORIZED));
                return;
            }
            // 保存上下文
            AuthUserContext.set(uid);
            chain.doFilter(req, resp);
        } catch (Exception e) {
            if (e instanceof YamiShopBindException) {
                httpHandler.printServerResponseToWeb((YamiShopBindException) e);
                return;
            }
            throw e;
        } finally {
            AuthUserContext.clean();
            PmsContext.clean();
        }
    }
    private void signAccess(FilterChain chain, HttpServletRequest req, HttpServletResponse resp, String dataType) throws ServletException, IOException {
        ServerResponseEntity<SignResponse> verifyResponse = signUtils.verify(req,dataType);
        SignResponse signResponse = verifyResponse.getData();
        long timestamp = System.currentTimeMillis();
        SysAccessKey sysAccessKey = signResponse.getSysAccessKey();
        if (!verifyResponse.isSuccess()) {
            verifyResponse.setSign(SignUtils.sign(signResponse.getAppSecret(), timestamp, null));
            verifyResponse.setTimestamp(timestamp);
            verifyResponse.setData(null);
            httpHandler.printServerResponseToWeb(verifyResponse);
            return;
        }
        Map<String, Object> dataMap = signResponse.getDataMap();
        String id = dataMap.containsKey(SignUtils.USER_ID) ? dataMap.get(SignUtils.USER_ID).toString() : dataMap.get(SignUtils.SHOP_ID).toString();
        UidInfoBO uidInfoBO = new UidInfoBO(SysTypeEnum.instance(sysAccessKey.getSysType()), id);
        // 还没有提供门店接口的服务
        if (Objects.equals(sysAccessKey.getSysType(), SysTypeEnum.MULTISHOP.value())) {
            uidInfoBO.setShopId(Long.valueOf(dataMap.get("shopId").toString()));
            uidInfoBO.setAdmin(0);
        }
        if (Objects.equals(sysAccessKey.getSysType(), SysTypeEnum.PLATFORM.value())) {
            uidInfoBO.setShopId(Constant.PLATFORM_SHOP_ID);
            uidInfoBO.setAdmin(0);
        }
//        // 商家或者平台要做权限校验
//        if (Objects.equals(sysAccessKey.getSysType(), SysTypeEnum.MULTISHOP.value())
//                || Objects.equals(sysAccessKey.getSysType(), SysTypeEnum.PLATFORM.value())) {
//            PmsContext.set(sysAccessKey.getPrems());
//        }
        ResponseWrapper responseWrapper = new ResponseWrapper(resp);
        AuthUserContext.set(AuthUserContext.getUid(uidInfoBO));
        chain.doFilter(signResponse.getReq(), responseWrapper);
        byte[] content = responseWrapper.getContent();
        // 获取相应数据
        String data = null;
        if (content.length > 0) {
            data = new String(content, StandardCharsets.UTF_8);
        }
        ServerResponseEntity<String> successResponse;
        if (StrUtil.isNotBlank(data)) {
            successResponse = Json.parseObject(data, ServerResponseEntity.class);
        } else if (responseWrapper.getStatus() != Constant.SUCCESS_CODE) {
            successResponse = ServerResponseEntity.showFailMsg(data);
        } else {
            successResponse = ServerResponseEntity.success(data);
        }
        String responseData = objectMapper.writeValueAsString(successResponse.getData());
        successResponse.setSign(SignUtils.sign(signResponse.getAppSecret(), timestamp, responseData));
        successResponse.setTimestamp(timestamp);
        successResponse.setData(responseData);
        httpHandler.printServerResponseToWeb(successResponse);
    }
}
